package com.ericsson.epc.modules.trace.task;

import com.ericsson.common.mapper.JsonMapper;
import com.ericsson.common.quartz.task.ManualTask;
import com.ericsson.common.util.DateUtils;
import com.ericsson.common.util.FileSizeHelper;
import com.ericsson.common.util.FileUtil;
import com.ericsson.common.util.LogFileName;
import com.ericsson.common.util.LoggerUtils;
import com.ericsson.common.util.StreamGobbler;
import com.ericsson.epc.modules.sys.entity.EPCNetElement;
import com.ericsson.epc.modules.sys.entity.EpcDic;
import com.ericsson.epc.modules.sys.service.EPCNetElementService;
import com.ericsson.epc.modules.sys.service.EpcDicService;
import com.ericsson.epc.modules.trace.entity.NodeInfo;
import com.ericsson.epc.modules.trace.entity.PoolNetInfo;
import com.ericsson.epc.modules.trace.entity.SignalFile;
import com.ericsson.epc.modules.trace.entity.SignalFileHistory;
import com.ericsson.epc.modules.trace.entity.UserSignalInfoEntity;
import com.ericsson.epc.modules.trace.service.SignalFileHistoryService;
import com.ericsson.epc.modules.trace.service.SignalFileService;
import com.fasterxml.jackson.databind.JavaType;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhuguangrui
 */
@Component
public class SignallingTraceTask implements ManualTask {

    private final Logger log = LoggerUtils.Logger(LogFileName.SignallingTraceJob);

    @Autowired
    private EpcDicService epcDicService;
    @Autowired
    private EPCNetElementService epcNetElementService;
    @Autowired
    private SignalFileService signalFileService;
    @Autowired
    private SignalFileHistoryService signalFileHistoryService;

    @Override
    public void execute() {
        int currentHour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
        if(currentHour == 0) {
            currentHour = 24;
        }

        String retentionDays = epcDicService.selectEpcDic(new EpcDic("dataRetentionDays")).getValue();
        Calendar calendar = Calendar.getInstance();
        //设置需删除的日期标志
        calendar.set(Calendar.DAY_OF_YEAR,calendar.get(Calendar.DAY_OF_YEAR) - Integer.parseInt(retentionDays));
        String dayFlag = new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime());
        deleteExpiredDir(dayFlag);

        log.info("==============SignallingTraceTask start================hour" + currentHour);
        long start = System.currentTimeMillis();
        String maxLength = epcDicService.selectEpcDic(new EpcDic("pcapMaxLength")).getValue();

        List<SignalFile> allSignalFile = signalFileService.findList(null);
        if(allSignalFile != null && allSignalFile.size() > 0) {
            for(SignalFile signalFile : allSignalFile) {
                //删除mysql过期的数据
                signalFileHistoryService.deleteExpiredData(signalFile.getId(),dayFlag);
            }
        }

        SignalFile tignalFile = new SignalFile();
        tignalFile.setStatus("1");
        List<SignalFile> signalFileList = signalFileService.findList(tignalFile);
        if (signalFileList != null && signalFileList.size() > 0) {
            String datePath = DateUtils.getDate();
            PoolNetInfo mmeList = new PoolNetInfo();
            PoolNetInfo saegwList = new PoolNetInfo();
            PoolNetInfo pcrfList = new PoolNetInfo();
            //拼接imsi参数
            StringBuffer imsiBuffer = new StringBuffer();
            //拼接msisdn参数
            StringBuffer msisdnBuffer = new StringBuffer();
            //拼接dest_folder参数
            StringBuffer destFolderBuffer = new StringBuffer();
            Map<String, Map<String, NodeInfo>> nodeInfoMap = new HashMap<>();
            for (SignalFile signalFileItem : signalFileList) {
                //判断当前号码的抓包时长是否达到至少15分钟
                if (!durationMinute(signalFileItem.getStarttime())) {
                    continue;
                }
                imsiBuffer.append(" ").append(signalFileItem.getImsi());
                msisdnBuffer.append(" ").append(signalFileItem.getMsisdn());
                destFolderBuffer.append(" ").append("/opt/Ericsson/core/signal/files/" + signalFileItem.getId() + "/" + datePath+"/"+currentHour);
                File file = new File("/opt/Ericsson/core/signal/files/" + signalFileItem.getId() + "/" + datePath+"/"+currentHour);
                if (!file.exists()) {
                    file.mkdirs();
                }
                //开始准备json文件
                UserSignalInfoEntity userSignalInfo = null;
                try {
                    JavaType javaType = JsonMapper.getInstance().getTypeFactory().constructType(UserSignalInfoEntity.class);
                    userSignalInfo = JsonMapper.getInstance().readValue(signalFileItem.getJsontext(), javaType);
                    PoolNetInfo mmePool = userSignalInfo.getMmePool();
                    PoolNetInfo saegwPool = userSignalInfo.getSaegwPool();
                    PoolNetInfo pcrfPool = userSignalInfo.getPcrfPool();
                    loadNetList(mmeList, mmePool);
                    loadNetList(saegwList, saegwPool);
                    loadNetList(pcrfList, pcrfPool);
                    if (userSignalInfo.getNetType() != null && userSignalInfo.getNetType().contains("PCRF")) {
                        nodeInfoMap.put("pcrf", genNodeInfo(pcrfList));
                    }
                    if (userSignalInfo.getNetType() != null && userSignalInfo.getNetType().contains("USERFACE")) {
                        EPCNetElement newnetelement = epcNetElementService.findByName(userSignalInfo.getSaegwNetName());
                        nodeInfoMap.put("userface", genNodeInfo(newnetelement));
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            nodeInfoMap.put("mme", genNodeInfo(mmeList));
            nodeInfoMap.put("epg", genNodeInfo(saegwList));
            File fileTxt = new File("/opt/Ericsson/core/signal/files/cron/node_info.json");
            try {
                if (!fileTxt.exists()) {
                    fileTxt.getParentFile().mkdirs();
                    fileTxt.createNewFile();
                } else {
                    fileTxt.delete();
                    fileTxt.createNewFile();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            writeToFile(nodeInfoMap, fileTxt);
            long pcapCmdStart = System.currentTimeMillis();
            List<String> pcapPaths = executeMultiDownloadPcap(imsiBuffer.toString(), msisdnBuffer.toString(), destFolderBuffer.toString());
            log.info("===== download_parse_uetrace_files.py hour :" +currentHour+ " use :" + (System.currentTimeMillis() - pcapCmdStart) / 60000 + "min");
            //更新pcap包至历史表
            Map<String,Boolean> needUpdateMap = new HashMap<>();
            for (SignalFile signalFileItem : signalFileList) {
                for (String pcapPath : pcapPaths) {
                    if (pcapPath.contains(signalFileItem.getImsi())) {
                        File pcapFile = new File(pcapPath);
                        if (pcapFile.exists() && pcapFile.isFile()) {
                            //此情况为特殊情况，当定时任务已经开启暂未执行完时，用户在页面点击停止抓包，用户点击的抓包会比定时任务提前完成。
                            SignalFileHistory signalFileHistory = queryHistoryOne(signalFileItem.getId(), datePath, currentHour);
                            if(signalFileHistory == null) {
                                needUpdateMap.put(signalFileItem.getId(),true);
                                //当前日期，当前小时的pcap文件入库
                                saveSignalFileHistory(signalFileItem.getId(), pcapPath, null, null, datePath, currentHour, FileSizeHelper.getHumanReadableFileSize(pcapFile.length()));
                            }else{
                                needUpdateMap.put(signalFileItem.getId(),false);
                            }
                        }
                        //删除中间过程产生的临时文件
                        deleteExpiredFiles(pcapPath);
                    }
                }
            }
            long genChartCmdStart = System.currentTimeMillis();
            String cmdParams = epcDicService.selectEpcDic(new EpcDic("filterGenChartParam")).getValue();
            //生成流程图
            for (SignalFile signalFileItem : signalFileList) {
                for (String pcapPath : pcapPaths) {
                    if (pcapPath.contains(signalFileItem.getImsi())) {
                        File pcapFile = new File(pcapPath);
                        if (pcapFile.exists() && pcapFile.isFile() && pcapFile.length() < 1024 * 1024 * Long.valueOf(maxLength)) {
                           //保证pcap包与流程图是匹配的
                            if(needUpdateMap.get(signalFileItem.getId())) {
                               long execChartStart = System.currentTimeMillis();
                               String htmlPath = executeFlowChart(pcapPath, signalFileItem.getMsisdn(), signalFileItem.getId(), datePath,currentHour, cmdParams);
                               //ebm信令聚合追踪流程图
                               //String ebmHtmlPath = executeEbmFlowChart(pcapPath,signalFileItem.getMsisdn(), Jdk8DateUtils.yesterday().formatDateTime("yyyyMMdd000000"),Jdk8DateUtils.yesterday().formatDateTime("yyyyMMdd235959"),null,Global.getConfig("signal.ebm.static.resources"),signalFileItem.getId(),datePath);
                               SignalFileHistory signalFileHistory = queryHistoryOne(signalFileItem.getId(), datePath, currentHour);

                               if (signalFileHistory != null && StringUtils.isNotBlank(htmlPath)) {
                                   File htmlFile = new File(htmlPath);
                                   if (htmlFile.exists() && htmlFile.isFile()) {
                                       signalFileHistory.setHtmlsize(FileSizeHelper.getHumanReadableFileSize(htmlFile.length()));
                                   }
                                   signalFileHistory.setHtmlusetime((System.currentTimeMillis() - execChartStart) / 60000);
                                   signalFileHistory.setHtmlpath(htmlPath);
                                   signalFileHistoryService.update(signalFileHistory);
                               }
                           }
                        } else {
                            log.info(pcapPath + "过大或文件不存在，不进行处理 hour:" +currentHour+ "文件大小为：" + FileSizeHelper.getHumanReadableFileSize(pcapFile.length()));
                        }
                    }
                }
            }
            log.info("==== filter_genchart.pl hour :" +currentHour+ "use :" + (System.currentTimeMillis() - genChartCmdStart) / 60000 + "min");
        }
        log.info("==============SignallingTraceTask hour :" +currentHour+ "=====end  all finish use :" + (System.currentTimeMillis() - start) / 60000 + "min");
    }


    private void deleteExpiredDir(String dayFlag) {
        File rootPath = new File("/opt/Ericsson/core/signal/files/");
        if (rootPath.exists() && rootPath.isDirectory()) {
            File[] fileIdPath = rootPath.listFiles();
            for (File idPath : fileIdPath) {
                if(idPath.exists() && idPath.isDirectory()) {
                    File[] fileDatePath = idPath.listFiles();
                    for(File datePath : fileDatePath) {
                        //清除过期的文件目录
                        if(datePath.isDirectory() && datePath.getName().compareTo(dayFlag) < 0){
                            FileUtil.deleteDir(datePath);
                            log.info("delete Expired path is : "+datePath);
                        }
                    }
                    File[] subFiles = idPath.listFiles();
                    if(subFiles == null || subFiles.length == 0 || (subFiles.length == 1 && subFiles[0].isFile())) {
                        FileUtil.deleteDir(idPath);
                    }
                }
            }

        }
    }

    /**
     * 判断抓包时长是否达到1小时以上
     *
     * @param startTime
     * @return
     */
    private static boolean durationMinute(String startTime) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            return (System.currentTimeMillis() - sdf.parse(startTime).getTime()) >= 60 * 15 * 1000;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 拼装池组信息
     *
     * @param netList
     * @param poolNetInfo
     */
    private static void loadNetList(PoolNetInfo netList, PoolNetInfo poolNetInfo) {
        for (int i = 0; i < poolNetInfo.getNetNames().size(); i++) {
            if (!netList.getNetNames().contains(poolNetInfo.getNetNames().get(i))) {
                netList.getNetNames().add(poolNetInfo.getNetNames().get(i));
                netList.getNetIps().add(poolNetInfo.getNetIps().get(i));
                if (StringUtils.isEmpty(netList.getUsername())) {
                    netList.setUsername(poolNetInfo.getUsername());
                }
                if (StringUtils.isEmpty(netList.getPassword())) {
                    netList.setPassword(poolNetInfo.getPassword());
                }
            }
        }
    }

    /**
     * 为json文件准备节点信息
     *
     * @param poolInfo 池组信息
     * @return
     */
    private static Map<String, NodeInfo> genNodeInfo(PoolNetInfo poolInfo) {
        Map<String, NodeInfo> nodeInfoMap = new HashMap<>();
        if (poolInfo.getNetNames().size() > 0) {
            for (int i = 0; i < poolInfo.getNetNames().size(); i++) {
                NodeInfo nodeItem = new NodeInfo();
                nodeItem.setHost(poolInfo.getNetIps().get(i));
                nodeItem.setUsername(poolInfo.getUsername());
                nodeItem.setPassword(poolInfo.getPassword());
                nodeInfoMap.put(poolInfo.getNetNames().get(i), nodeItem);
            }
        }
        return nodeInfoMap;
    }

    /**
     * 为json文件准备节点信息
     *
     * @param epcNetElement 网元信息
     * @return
     */
    private static Map<String, NodeInfo> genNodeInfo(EPCNetElement epcNetElement) {
        Map<String, NodeInfo> nodeInfoMap = new HashMap<>();
        if (epcNetElement != null) {
            NodeInfo nodeItem = new NodeInfo();
            nodeItem.setHost(epcNetElement.getIpadr());
            nodeItem.setUsername(epcNetElement.getUsername1());
            nodeItem.setPassword(epcNetElement.getPassword1());
            nodeInfoMap.put(epcNetElement.getFname(), nodeItem);
        }
        return nodeInfoMap;
    }

    /**
     * 写JSON数据到文件
     *
     * @param content
     * @param file
     */
    private static void writeToFile(Object content, File file) {
        FileWriter fw = null;
        PrintWriter pw = null;
        try {
            if (file.exists()) {
                fw = new FileWriter(file, true);
                pw = new PrintWriter(fw);
                pw.println(JsonMapper.toJsonString(content));
                pw.flush();
                fw.flush();
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (pw != null) {
                pw.close();
            }
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 执行批量号码抓包文件下载脚本
     *
     * @param imsi
     * @param msisdn
     * @param descFolder
     * @return
     */
    private List<String> executeMultiDownloadPcap(String imsi, String msisdn, String descFolder) {
        List<String> pcapNames = new ArrayList<>();
        Process proc;
        String[] cmd = new String[3];
        //批量号码time_span参数逻辑 零点至当前时间
        cmd[0] = "sh";
        cmd[1] = "-c";
        cmd[2] = "python /opt/Ericsson/core/bin/download_parse_uetrace_files.py " +
                " -node_info /opt/Ericsson/core/signal/files/cron/node_info.json" +
                " -imsi " + imsi + " -msisdn " + msisdn + " -dest_folder " + descFolder +
                " -time_span 3900";
        log.info(cmd[2]);
        try {
            proc = Runtime.getRuntime().exec(cmd);
            BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String line;
            while ((line = br.readLine()) != null) {
                if (line.trim().startsWith("/opt/Ericsson/core/signal/files") && line.contains("-") && line.contains(".pcap") && !line.contains("userface")) {
                    pcapNames.add(line.trim());
                }
            }
            br.close();
            proc.waitFor();
            proc.destroy();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return pcapNames;
    }

    /**
     * 根据pid和日期查询抓包历史记录
     *
     * @param pid
     * @param datePath
     * @return
     */
    private SignalFileHistory queryHistoryOne(String pid, String datePath, int createHour) {
        SignalFileHistory signalFileHistory = new SignalFileHistory();
        signalFileHistory.setPid(pid);
        signalFileHistory.setCreateday(datePath);
        signalFileHistory.setCreatehour(createHour);
        List<SignalFileHistory> fileHistoryList = signalFileHistoryService.findList(signalFileHistory);
        if (fileHistoryList != null && fileHistoryList.size() > 0) {
            return fileHistoryList.get(0);
        }
        return null;
    }

    /**
     * 保存抓包历史
     *
     * @param pid
     * @param pcapPath
     * @param htmlPath
     * @param ebmHtmlPath
     * @param datePath
     */
    private void saveSignalFileHistory(String pid, String pcapPath, String htmlPath, String ebmHtmlPath, String datePath, int createHour, String capSize) {
        SignalFileHistory signalFileHistory = new SignalFileHistory();
        signalFileHistory.setPid(pid);
        signalFileHistory.setCappath(pcapPath);
        signalFileHistory.setHtmlpath(htmlPath);
        signalFileHistory.setEbmhtmlpath(ebmHtmlPath);
        signalFileHistory.setCreateday(datePath);
        signalFileHistory.setCreatehour(createHour);
        signalFileHistory.setCapsize(capSize);
        signalFileHistoryService.insert(signalFileHistory);
    }

    /**
     * 删除当前包所在目录下过期的抓包文件
     *
     * @param pcapPath
     */
    private static void deleteExpiredFiles(String pcapPath) {
        if (StringUtils.isNotBlank(pcapPath)) {
            File parentPath = new File(new File(pcapPath).getParent());
            if (parentPath.isDirectory()) {
                File[] files = parentPath.listFiles();
                for (File file : files) {
                    if (!matchPcapName(file.getName()) && !file.getName().endsWith(".html")) {
                        file.delete();
                    }
                }
            }
        }
    }

    /**
     * 执行脚本filter_genchart.pl，根据抓包文件生成流程图
     *
     * @param pcapName pcap包路径
     * @param msisdn   用户手机号
     * @param fileId   抓包号码对应的ID
     * @param datePath 抓包日期
     * @return 返回流程图路径
     */
    private String executeFlowChart(String pcapName, String msisdn, String fileId, String datePath,int hour, String opt) {

        String htmlPath = null;
        Process proc;
        String[] cmd = new String[3];
        cmd[0] = "sh";
        cmd[1] = "-c";
        cmd[2] = "/opt/Ericsson/core/bin/filter_genchart.pl " +
                " -hostfile /opt/Ericsson/core/bin/tracehosts -src " + pcapName + " -destpath /opt/Ericsson/core/signal/files/" + fileId + "/" + datePath + "/" + hour
                + " -msisdn " + msisdn;
        if (StringUtils.isNotBlank(opt)) {
            cmd[2] += " -tshark_opt '" + opt + "'";
        }
        log.info(cmd[2]);
        try {
            File file = new File("/opt/Ericsson/core/signal/files/" + fileId + "/" + datePath + "/" + hour);
            if (!file.exists()) {
                file.mkdirs();
            }
            proc = Runtime.getRuntime().exec(cmd);
            BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            StreamGobbler outputGobbler = new StreamGobbler(proc.getErrorStream(),"error",null);
            outputGobbler.start();
            String line;
            while ((line = br.readLine()) != null) {
                //log.info("filter_genchart.pl result line====" + line);
                if (line.contains(".html")) {
                    htmlPath = line.trim();
                    log.info("chart：{" + htmlPath + "}");
                }
            }
            br.close();
            proc.waitFor();
            proc.destroy();
        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
        }
        if (StringUtils.isNotBlank(htmlPath)) {
            return htmlPath;
        }
        return null;
    }

    private static boolean matchPcapName(String pcapName) {
        if (StringUtils.isNotBlank(pcapName)) {
            String pattern = "\\d{10,}-\\d+.pcap";
            Pattern r = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
            Matcher m = r.matcher(pcapName);
            return m.find();
        }
        return false;
    }
}
